/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    sfud_def.h
 *  @brief   Macro definition Header File of SFUD
 *  @version v1.0
 *  @date    03. Apr. 2023
 *  @author  gonght
 ****************************************************************/
#ifndef __SFUD_DEF_H__
#define __SFUD_DEF_H__

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "sfud_cfg.h"
#include "sfud_flash_def.h"

#ifdef __cplusplus
extern "C" {
#endif

#define SFUD_DEBUG_MODE
/* ../port/sfup_port.c */
void sfud_log_debug(const char *file, const long line, const char *format, ...);
void sfud_log_info(const char *format, ...);

/* debug print function. Must be implement by user. */
#ifdef SFUD_DEBUG_MODE
#define SFUD_DEBUG(fmt, ...) sfud_log_debug(__func__, __LINE__, fmt, ##__VA_ARGS__)
#define SFUD_INFO(fmt, ...) sfud_log_info(fmt, ##__VA_ARGS__)
#else
#define SFUD_DEBUG(fmt, ...)
#define SFUD_INFO(fmt, ...)
#endif /* SFUD_DEBUG_MODE */

/* assert for developer. */
#ifdef SFUD_DEBUG_MODE
#define SFUD_ASSERT(EXPR)                                                      \
	if (!(EXPR))                                                                   \
	{                                                                              \
		SFUD_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);          \
		while (1);                                                                 \
	}
#else
#define SFUD_ASSERT(EXPR)                                                      \
	if (!(EXPR))                                                                   \
	{                                                                              \
		SFUD_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);          \
		while (1);                                                                 \
	}
#endif

/**
 * retry process
 *
 * @param delay delay function for every retry. NULL will not delay for every retry.
 * @param retry retry counts
 * @param result SFUD_ERR_TIMEOUT: retry timeout
 */
#define SFUD_RETRY_PROCESS(delay, retry, result)                               \
	void (*__delay_temp)(void) = (void (*)(void))delay;                        \
	if (retry == 0) {result = SFUD_ERR_TIMEOUT;break;}                         \
	else {if (__delay_temp) {__delay_temp();} retry --;}

/* software version number */
#define SFUD_SW_VERSION                             "1.1.0"
/*
 * all defined supported command
 */
#ifndef SFUD_CMD_WRITE_ENABLE
#define SFUD_CMD_WRITE_ENABLE                          0x06
#endif

#ifndef SFUD_CMD_WRITE_DISABLE
#define SFUD_CMD_WRITE_DISABLE                         0x04
#endif

#ifndef SFUD_CMD_READ_STATUS_REGISTER1
#define SFUD_CMD_READ_STATUS_REGISTER1                 0x05
#endif

#ifndef SFUD_CMD_READ_STATUS_REGISTER2
#define SFUD_CMD_READ_STATUS_REGISTER2                 0x35
#endif

#ifndef SFUD_CMD_READ_VOL_CFG_REGISTER
#define SFUD_CMD_READ_VOL_CFG_REGISTER                 0x85
#endif

#ifndef SFUD_CMD_READ_NONVOL_CFG_REGISTER
#define SFUD_CMD_READ_NONVOL_CFG_REGISTER              0xB5
#endif

#ifndef SFUD_VOLATILE_SR_WRITE_ENABLE
#define SFUD_VOLATILE_SR_WRITE_ENABLE                  0x50
#endif

#ifndef SFUD_CMD_WRITE_STATUS_REGISTER
#define SFUD_CMD_WRITE_STATUS_REGISTER                 0x01
#endif

#ifndef SFUD_CMD_WRITE_STATUS_REGISTER2
#define SFUD_CMD_WRITE_STATUS_REGISTER2                0x31
#endif

#ifndef SFUD_CMD_WRITE_VOL_CFG_REGISTER
#define SFUD_CMD_WRITE_VOL_CFG_REGISTER                0x81
#endif

#ifndef SFUD_CMD_WRITE_NONVOL_CFG_REGISTER
#define SFUD_CMD_WRITE_NONVOL_CFG_REGISTER             0xB1
#endif

#ifndef SFUD_CMD_PAGE_PROGRAM
#define SFUD_CMD_PAGE_PROGRAM                          0x02
#endif

#ifndef SFUD_CMD_QUAD_PAGE_PROGRAM
#define SFUD_CMD_QUAD_PAGE_PROGRAM                     0x32
#endif

#ifndef SFUD_CMD_AAI_WORD_PROGRAM
#define SFUD_CMD_AAI_WORD_PROGRAM                      0xAD
#endif

#ifndef SFUD_CMD_ERASE_CHIP
#define SFUD_CMD_ERASE_CHIP                            0xC7
#endif

#ifndef SFUD_CMD_READ_DATA
#define SFUD_CMD_READ_DATA                             0x03
#endif

#ifndef SFUD_CMD_DUAL_OUTPUT_READ_DATA
#define SFUD_CMD_DUAL_OUTPUT_READ_DATA                 0x3B
#endif

#ifndef SFUD_CMD_DUAL_IO_READ_DATA
#define SFUD_CMD_DUAL_IO_READ_DATA                     0xBB
#endif

#ifndef SFUD_CMD_QUAD_IO_READ_DATA
#define SFUD_CMD_QUAD_IO_READ_DATA                     0xEB
#endif

#ifndef SFUD_CMD_QUAD_OUTPUT_READ_DATA
#define SFUD_CMD_QUAD_OUTPUT_READ_DATA                 0x6B
#endif

#ifndef SFUD_CMD_MANUFACTURER_DEVICE_ID
#define SFUD_CMD_MANUFACTURER_DEVICE_ID                0x90
#endif

#ifndef SFUD_CMD_JEDEC_ID
#define SFUD_CMD_JEDEC_ID                              0x9F
#endif

#ifndef SFUD_CMD_READ_UNIQUE_ID
#define SFUD_CMD_READ_UNIQUE_ID                        0x4B
#endif

#ifndef SFUD_CMD_READ_SFDP_REGISTER
#define SFUD_CMD_READ_SFDP_REGISTER                    0x5A
#endif

#ifndef SFUD_CMD_ENABLE_RESET
#define SFUD_CMD_ENABLE_RESET                          0x66
#endif

#ifndef SFUD_CMD_RESET
#define SFUD_CMD_RESET                                 0x99
#endif

#ifndef SFUD_CMD_ENTER_4B_ADDRESS_MODE
#define SFUD_CMD_ENTER_4B_ADDRESS_MODE                 0xB7
#endif

#ifndef SFUD_CMD_EXIT_4B_ADDRESS_MODE
#define SFUD_CMD_EXIT_4B_ADDRESS_MODE                  0xE9
#endif

#ifndef SFUD_WRITE_MAX_PAGE_SIZE
#define SFUD_WRITE_MAX_PAGE_SIZE                        256
#endif

/* send dummy data for read data */
#ifndef SFUD_DUMMY_DATA
#define SFUD_DUMMY_DATA                                0xFF
#endif

/* maximum number of erase type support on JESD216 (V1.0) */
#define SFUD_SFDP_ERASE_TYPE_MAX_NUM                      4

/**
 * status register bits
 */
enum {
	SFUD_STATUS_REGISTER_BUSY = (1 << 0),                  /**< busing */
	SFUD_STATUS_REGISTER_WEL = (1 << 1),                   /**< write enable latch */
	SFUD_STATUS_REGISTER_SRP = (1 << 7),                   /**< status register protect */
};

/**
 * error code
 */
typedef enum {
	SFUD_SUCCESS = 0,                                      /**< success */
	SFUD_ERR_NOT_FOUND = 1,                                /**< not found or not supported */
	SFUD_ERR_WRITE = 2,                                    /**< write error */
	SFUD_ERR_READ = 3,                                     /**< read error */
	SFUD_ERR_TIMEOUT = 4,                                  /**< timeout error */
	SFUD_ERR_ADDR_OUT_OF_BOUND = 5,                        /**< address is out of flash bound */
} sfud_err;

/**
 * QSPI flash read cmd format
 */
typedef struct {
	uint8_t instruction;
	uint8_t instruction_lines;
	uint8_t address_size;
	uint8_t address_lines;
	uint8_t alternate_bytes_lines;
	uint8_t dummy_cycles;
	uint8_t data_lines;
} sfud_qspi_read_cmd_format;

/* SPI bus write read data function type */
typedef sfud_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, size_t read_size);

/**
 * SPI device
 */
typedef struct __sfud_spi {
	/* SPI device name */
	const char *name;
	/* SPI write read function */
	sfud_err (*wr)(const struct __sfud_spi *spi, uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
			size_t read_size);
	/* QSPI write read function */
	sfud_err (*qspi_wr)(const struct __sfud_spi *spi, uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
			size_t read_size);
	/* lock SPI bus */
	void (*lock)(const struct __sfud_spi *spi);
	/* unlock SPI bus */
	void (*unlock)(const struct __sfud_spi *spi);
	/* some user data */
	void *user_data;
} sfud_spi, *sfud_spi_t;

typedef struct {
	void (*delay)(void);                     /**< every retry's delay */
	size_t times;                            /**< default times for error retry */
} sfud_retry;

/**
 * serial flash device
 */
typedef struct {
	const char *name;                            /**< serial flash name */
	size_t index;                                /**< index of flash device information table  @see flash_table */
	sfud_flash_chip chip;                        /**< flash chip information */
	sfud_spi spi;                                /**< SPI device */
	bool init_ok;                                /**< initialize OK flag */
	bool addr_in_4_byte;                         /**< flash is in 4-Byte addressing */

	sfud_retry retry;                            /**< default times for error retry */

	void *user_data;                             /**< some user data */
} sfud_flash, *sfud_flash_t;

#ifdef __cplusplus
}
#endif

#endif /* __SFUD_DEF_H__ */
